#include "dialogtesteventcapture.h"
#include "ui_dialogtesteventcapture.h"

DialogTestEventCapture::DialogTestEventCapture(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::DialogTestEventCapture)
{
    ui->setupUi(this);
    setWindowTitle("Capture Video Data For The Event");

    timer = new QTimer(this);
    timer->start(20);  //20 millisecond
    connect(timer, SIGNAL(timeout()), this, SLOT(TimerEvent()));

    ui->spinBox->setValue(10); //default to ten second
    ui->progressBar->setValue(0); //initialize at zero


    recording=0;
    showCams=false;  //initialy not showing cameras
}

DialogTestEventCapture::~DialogTestEventCapture()
{
    delete ui;
}

void DialogTestEventCapture::on_spinBox_valueChanged(int arg1)
{
    eventTime=arg1*1000;  //milliseconds for the event to run

    timecode.reserve(30*arg1*2);  //reserve enough for 30 FPS times two cameras

    ui->pushButton_Run->setDefault(true);
}

void DialogTestEventCapture::on_pushButton_Run_clicked()
{
    // start recording.  change pushbutton text to "stop"
    if(!recording){
       recording=1; //set recording to true

       ui->pushButton_Run->setText("Stop");
       ui->label_Cam0Frames->setText("Recording");
       ui->label_Cam1Frames->setText("Recording");
       startTime.start();   // initialize
       lasttime=0;          // initialize
       frameCounter=0;  //zero the frame counter
       
       grabTimeLast0 = 0;
       grabTimeLast1 = 0;
       minLatency0 = 10000;  //just a big number
       maxLatency0 = 0;
       minLatency1 = 10000; 
       maxLatency1 = 0;
       minLatency0to1 = 1000;
       maxLatency0to1 =0;

    } else {
        recording=0; //stop recording & Update button text
        ui->pushButton_Run->setText("Run");
        ui->label_Cam0Frames->setText("Stopped");
        ui->label_Cam1Frames->setText("Stopped");
    }


}

void DialogTestEventCapture::TimerEvent()
{

  currentTime=startTime.elapsed();  //milliseconds since we started
  int value = currentTime*100/eventTime;

  ui->progressBar->setValue(value);

  //cam0Future.waitForFinished();
  //if(capWebcam0.isOpened())capWebcam0.read(matWebcam);
  m0.lock(); // this will block until the writer0 is done
  capWebcam0.grab();
  if(recording) timecode.push_back(grabTime0 = startTime.elapsed());  //log the framegrab timecode for cam0
  m0.unlock();

  //cam1Future.waitForFinished();
  //if(capWebcam1.isOpened())capWebcam1.read(matWebcam_2);
  m1.lock(); // this will block until writer1 is done
  capWebcam1.grab();
  if(recording) timecode.push_back(grabTime1 = startTime.elapsed());  //log the framegrab timecode for cam1
  m1.unlock();

  if(!recording) { //pull them in for display, if I keep this move it down
      capWebcam0.retrieve(matWebcam);
      capWebcam1.retrieve(matWebcam_2);
  }

  if(recording) {  // write
      //video encoding
      int enc=CV_FOURCC('m','p','4','v');

      // Cam 1
      if (!record.isOpened())  // open file for writing video
          record.open(filename.toStdString(), enc, 30, matWebcam.size(), true);
      if (record.isOpened()) { // make sure we opened
          ui->label_Cam0Frames->setText("Recording");
          //record << matWebcam;
          //cam0Future = QtConcurrent::run(&record,&cv::VideoWriter::write,matWebcam);
          m0.lock();  //unlock is done in the thread
          emit write0();
      } else
          ui->label_Cam0Frames->setText("Record Failed");

      // Cam 2
      if (!record_2.isOpened()) // open file for writing video
          record_2.open(filename_2.toStdString(), enc, 30, matWebcam_2.size(), true);
      if (record_2.isOpened()) { // make sure we opened
          ui->label_Cam1Frames->setText("Recording");
          //record_2 << matWebcam_2;
          //cam1Future = QtConcurrent::run(&record_2,&cv::VideoWriter::write,matWebcam_2);
          m1.lock(); //unlock is done in the thread
          emit write1();
      } else
          ui->label_Cam1Frames->setText("Record Failed");


      ui->label_FramesGrabbed->setText(QString::number(frameCounter++));
  }
  if(!recording && showCams) {  //If not recording, show the video feed from the cameras (unless toggled to not show)
      if(!matWebcam.empty()) {
          cvtColor(matWebcam,matWebcam,CV_BGR2RGB);
          QImage qimgVid((uchar*)matWebcam.data,matWebcam.cols,matWebcam.rows,
                         matWebcam.step,QImage::Format_RGB888);
          int w = ui->label_Cam0->width();
          ui->label_Cam0->setPixmap(QPixmap::fromImage(qimgVid).scaledToWidth(w,Qt::FastTransformation));
      }
      if(!matWebcam_2.empty()) {
          cvtColor(matWebcam_2,matWebcam_2,CV_BGR2RGB);
          QImage qimgVid((uchar*)matWebcam_2.data,matWebcam_2.cols,matWebcam_2.rows,
                         matWebcam_2.step,QImage::Format_RGB888);
          int w = ui->label_Cam1->width();
          ui->label_Cam1->setPixmap(QPixmap::fromImage(qimgVid).scaledToWidth(w,Qt::FastTransformation));
      }
  }

  if(recording && currentTime>=eventTime) { //we are recording and we hit the time limit
      cv::FileStorage fs(QString(dirVideo.path()+"/VideoInfo.yml").toStdString(), cv::FileStorage::WRITE);
      if(fs.isOpened()){
          fs << "EventTime" << startTime.elapsed();  //time in milliseconds
          fs << "FramesGrabbed" << frameCounter;
          fs << "TimeCode" <<timecode;
          fs.release();
      }
      recording=0;
      // if recording files are open, close them
      cam0Future.waitForFinished();
      cam1Future.waitForFinished();

      //block until the last frames are written
      emit close0();
      emit close1();

//      if (record.isOpened()) record.release();
//      if (record_2.isOpened()) record_2.release();

      ui->pushButton_Run->setText("Run");
      ui->pushButton_Run->setEnabled(false); //now that we have files disable unless overwite is selected
      ui->checkBox->setEnabled(true); //set overwrite check to enabled
      ui->checkBox->setChecked(false); //set value of overwrite to false
      ui->pushButton_Run->setToolTip("Files exist. Set overwrite to enable run");

  }

  if(fpsUpdateCounter++==frameMax) {

      fps();  //update FPS display

      if (recording) {
          //cam0 latency calcs
          int latency = (grabTime0 - grabTimeLast0)/frameMax;
          ui->label_Latency0->setText(QString::number( latency ).rightJustified(4, ' '));
          if (latency > maxLatency0) {
              maxLatency0 = latency;
              ui->label_maxLatency0->setText(QString::number( maxLatency0 ).rightJustified(4, ' ') );

          }
          if(latency<minLatency0) {
              minLatency0 = latency;
              ui->label_minLatency0->setText(QString::number( minLatency0 ).rightJustified(4, ' ') );
          }
          //cam1 latency calcs
          latency = (grabTime1 - grabTimeLast1)/frameMax;
          ui->label_Latency1->setText(QString::number( latency ).rightJustified(4, ' '));
          if (latency > maxLatency1) {
              maxLatency1 = latency;
              ui->label_maxLatency1->setText(QString::number( maxLatency1 ).rightJustified(4, ' ') );

          }
          if(latency<minLatency1) {
              minLatency1 = latency;
              ui->label_minLatency1->setText(QString::number( minLatency1 ).rightJustified(4, ' ') );
          }

          //cam0 to cam1 latency calcs
          latency = grabTime1 - grabTime0;
          ui->label_Latency0to1->setText(QString::number( latency ).rightJustified(4, ' '));
          if (latency > maxLatency0to1) {
              maxLatency0to1 = latency;
              ui->label_maxLatency0to1->setText(QString::number( maxLatency0to1 ).rightJustified(4, ' ') );

          }
          if(latency<minLatency0to1) {
              minLatency0to1 = latency;
              ui->label_minLatency0to1->setText(QString::number( minLatency0to1 ).rightJustified(4, ' ') );
          }
      grabTimeLast0=grabTime0;
      grabTimeLast1=grabTime1;

      }
    }

}


void DialogTestEventCapture::init(QString event) {
    //make directory if not done already
    dirVideo.setPath(event + "/VideoFiles");
    if (!dirVideo.exists())
        dirVideo.mkpath(".");

    // Set up the cameras
        // Camera 0
    capWebcam0.open(0);
    capWebcam0.set(CV_CAP_PROP_FRAME_WIDTH, resWidth);      //Cam Width
    capWebcam0.set(CV_CAP_PROP_FRAME_HEIGHT, resHeight);      //Cam Height
    if(capWebcam0.isOpened()==false){
        ui->label_Cam0Frames->setText("Failed Open");

    } else
        ui->label_Cam0Frames->setText("Ready");

        // Camera 1
    capWebcam1.open(1);
    capWebcam1.set(CV_CAP_PROP_FRAME_WIDTH, resWidth);      //Cam Width
    capWebcam1.set(CV_CAP_PROP_FRAME_HEIGHT, resHeight);      //Cam Height
    if(capWebcam1.isOpened()==false){
        ui->label_Cam1Frames->setText("Failed Open");
    } else
        ui->label_Cam1Frames->setText("Ready");

    // File names for recording
    filename=QString(dirVideo.path()+"/Cam0.avi");
    filename_2=QString(dirVideo.path()+"/Cam1.avi");

    //check for existing video files
    if(QFile(filename).exists() ||QFile(filename).exists()) {
        ui->pushButton_Run->setEnabled(false);  //disable the run button for now
        ui->pushButton_Run->setToolTip("Files exist. Set overwrite to enable run");
    } else {
        ui->checkBox->setEnabled(false);  //no need (yet) if no files exist,
    }



    //fps counters
    frameMax =5;   //update FPS counter every X frames
    fpsUpdateCounter=0;  //running counter. update when frameCount=frameMax.


    frameCounter=0;

    //grab first frame, used to get the camera dimensions for the writer
    if(capWebcam0.isOpened())capWebcam0.read(matWebcam);
    if(capWebcam1.isOpened())capWebcam1.read(matWebcam_2);

    //setup threads & objects inside them
    thread0 = new QThread;
    thread1 = new QThread;
    writer0 = new writer(&capWebcam0, &record, &m0);
    writer1 = new writer(&capWebcam1, &record_2, &m1);
    writer0->moveToThread(thread0);
    writer1->moveToThread(thread1);
    connect(this, SIGNAL(write0()), writer0, SLOT(writeFrame()),Qt::QueuedConnection);
    connect(this, SIGNAL(write1()), writer1, SLOT(writeFrame()),Qt::QueuedConnection);
    connect(this, SIGNAL(close0()), writer0, SLOT(Close()),Qt::QueuedConnection);
    connect(this, SIGNAL(close1()), writer1, SLOT(Close()),Qt::QueuedConnection);
    thread0->start();
    thread1->start();

}


void DialogTestEventCapture::fps (){
    time=startTime.elapsed();   //milliseconds since start
    timesecs=(float) (time-lasttime)/1000;  //convert to seconds
    lasttime=time;   //save time for next iteration

    ui->label_FramesPerSec->setText(QString::number((float)fpsUpdateCounter/timesecs ,'f',2).rightJustified(7, ' ') );
    fpsUpdateCounter = 0; //reset the counter
}

void DialogTestEventCapture::on_checkBox_clicked(bool checked)
{
    if(checked){
        ui->pushButton_Run->setEnabled(true);  //enable the run button
        ui->pushButton_Run->setToolTip("");
    } else {
        ui->pushButton_Run->setEnabled(false);  //disable the run button
        ui->pushButton_Run->setToolTip("Files exist. Set overwrite to enable run");
    }

}

void DialogTestEventCapture::on_checkBox_ShowCams_clicked(bool checked)
{
    showCams=checked;
}

/// writer class constructor
writer::writer(cv::VideoCapture *cV, cv::VideoWriter *wV, QMutex *mut)
{
    capVid=cV;
    writeVid=wV;
    m=mut;
}

void writer::writeFrame()
{
    capVid->retrieve(matCap);
    m->unlock();

    if(matCap.empty()) qDebug()<< "Empty Mat!  Retrieve Failed";

    if(!writeVid->isOpened()) qDebug()<< "VideoWriter is not Opened!!";
    else writeVid->write(matCap);


}

void writer::Close() 
{
    writeVid->release();
}
